import pandas as pd
import numpy as np
import re
import hashlib
from datetime import datetime
import openpyxl
from openpyxl.utils.dataframe import dataframe_to_rows
import os
import logging
import unicodedata

# 配置日志
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(levelname)s - %(message)s',
    handlers=[
        logging.FileHandler('policy_data_cleaning.log'),
        logging.StreamHandler()
    ]
)


def deduplicate_policy_ids(df):
    """删除重复政策ID的行，保留第一个出现的行"""
    logger = logging.getLogger('deduplicate_policy_ids')

    if '政策ID' not in df.columns:
        logger.warning("未找到'政策ID'列，无法进行去重操作")
        return df

    # 记录去重前的行数
    original_rows = len(df)

    # 找出重复的政策ID
    duplicate_mask = df.duplicated(subset='政策ID', keep='first')
    duplicate_count = duplicate_mask.sum()

    if duplicate_count > 0:
        # 删除重复行（保留第一个出现的行）
        df = df[~duplicate_mask].copy()
        logger.info(f"已删除 {duplicate_count} 条重复政策ID的记录")
        logger.info(f"去重后剩余 {len(df)} 行")
    else:
        logger.info("未发现重复的政策ID")

    return df


def remove_invalid_rows(df):
    """删除标题列或正文列为空的行"""
    logger = logging.getLogger('remove_invalid_rows')

    required_columns = ['标题', '正文']
    missing_columns = [col for col in required_columns if col not in df.columns]

    if missing_columns:
        logger.warning(f"缺少必要的列: {', '.join(missing_columns)}，无法执行删除无效行操作")
        return df

    # 记录删除前行数
    original_rows = len(df)

    # 定义更严格的空值判断函数
    def is_blank(value):
        if pd.isna(value):
            return True
        if isinstance(value, str):
            # 处理空字符串、空格、换行符等
            return value.strip() == '' or value.strip() == 'nan'
        return False

    # 删除标题或正文为空的行
    invalid_mask = df['标题'].apply(is_blank) | df['正文'].apply(is_blank)
    invalid_count = invalid_mask.sum()

    if invalid_count > 0:
        df = df[~invalid_mask].copy()
        logger.info(f"已删除 {invalid_count} 条标题或正文为空的记录")
        logger.info(f"删除后剩余 {len(df)} 行")
    else:
        logger.info("未发现标题或正文为空的记录")

    return df


def check_and_add_crossborder_column(df):
    """检查并添加'是否是跨境政策'列"""
    logger = logging.getLogger('add_crossborder_column')

    # 定义目标列名
    target_column = '是否是跨境政策'

    # 检查列是否存在
    if target_column not in df.columns:
        logger.info(f"'{target_column}'列不存在，正在添加...")

        # 添加新列并设置默认值
        df[target_column] = ""  # 默认值为空（否）
        logger.info(f"已添加'{target_column}'列，默认值设为空")

    else:
        logger.info(f"'{target_column}'列已存在")

    return df


def clean_dates(df, date_columns):
    """清洗和格式化日期列"""
    logger = logging.getLogger('clean_dates')

    # 日期格式模式
    date_patterns = [
        r'(\d{4})[年./-](\d{1,2})[月./-](\d{1,2})日?',  # yyyy-mm-dd
        r'(\d{4})[年./-](\d{1,2})月?',  # yyyy-mm
        r'(\d{4})年'  # yyyy
    ]

    for col in date_columns:
        if col not in df.columns:
            logger.warning(f"日期列 '{col}' 不存在，跳过")
            continue

        logger.info(f"清洗日期列: {col}")
        clean_dates = []
        valid_count = 0

        for value in df[col]:
            # 处理空值
            if pd.isna(value) or value in ['', ' ', '无', 'null', 'None']:
                clean_dates.append(None)
                continue

            # 尝试转换为日期对象
            if isinstance(value, datetime) or isinstance(value, pd.Timestamp):
                clean_dates.append(value.strftime('%Y-%m-%d'))
                valid_count += 1
                continue

            # 处理字符串格式的日期
            value_str = str(value).strip()
            found = False

            # 尝试匹配各种日期格式
            for pattern in date_patterns:
                match = re.match(pattern, value_str)
                if match:
                    groups = match.groups()
                    year = groups[0]
                    month = groups[1] if len(groups) > 1 else '01'
                    day = groups[2] if len(groups) > 2 else '01'

                    # 补零
                    month = month.zfill(2)
                    day = day.zfill(2)

                    # 创建日期字符串
                    date_str = f"{year}-{month}-{day}"

                    # 验证日期有效性
                    try:
                        datetime.strptime(date_str, '%Y-%m-%d')
                        clean_dates.append(date_str)
                        valid_count += 1
                        found = True
                        break
                    except ValueError:
                        continue

            if not found:
                clean_dates.append(None)
                logger.debug(f"无法解析日期: {value_str}")

        # 更新列数据
        df[col] = clean_dates
        logger.info(f"日期列 '{col}' 清洗完成: 有效日期 {valid_count}/{len(df)}")

    return df


def remove_whitespace(df):
    """删除所有空格和空行"""
    logger = logging.getLogger('remove_whitespace')

    # 删除空行
    original_rows = len(df)
    df.replace('', np.nan, inplace=True)
    df.dropna(how='all', inplace=True)
    new_rows = len(df)
    logger.info(f"删除空行: 原始行数 {original_rows}, 当前行数 {new_rows}")

    # 删除所有空格和特殊空白字符
    for col in df.columns:
        # 只处理字符串列
        if df[col].dtype == 'object':
            df[col] = df[col].apply(
                lambda x: ''.join(str(x).split()) if pd.notna(x) else x
            )

    logger.info("已移除所有空格和空白字符")
    return df


def generate_policy_id(df):
    """生成政策ID并放置在第一列（如果不存在）"""
    logger = logging.getLogger('generate_policy_id')

    # 检查政策ID列是否已存在
    if '政策ID' in df.columns:
        logger.info("'政策ID'列已存在，跳过生成")
        return df

    if '发文日期' not in df.columns:
        logger.error("缺少 '发文日期' 列，无法生成政策ID")
        return df

    if '标题' not in df.columns:
        logger.error("缺少 '标题' 列，无法生成政策ID")
        return df

    policy_ids = []

    for idx, row in df.iterrows():
        title = str(row['标题']) if pd.notna(row['标题']) else ''
        publish_date = str(row['发文日期']) if pd.notna(row['发文日期']) else ''

        # 处理空值
        if not title and not publish_date:
            policy_ids.append(None)
            continue

        # 提取发文日期的数字部分 (yyyy-mm-dd -> yyyymmdd)
        date_digits = ''.join(re.findall(r'\d', publish_date))[:8]
        if len(date_digits) < 8:
            date_digits = date_digits.ljust(8, '0')[:8]

        # 创建MD5哈希
        hash_input = f"{title}_{date_digits}"

        # 标准化Unicode字符（解决中文编码问题）
        normalized_input = unicodedata.normalize('NFKC', hash_input)
        md5_hash = hashlib.md5(normalized_input.encode('utf-8')).hexdigest()

        # 生成政策ID
        policy_id = f"P_{date_digits}_{md5_hash}"
        policy_ids.append(policy_id)

    # 添加到DataFrame并放置在第一列
    df.insert(0, '政策ID', policy_ids)
    logger.info(f"已生成 {len([x for x in policy_ids if x])} 个政策ID，并放置在第一列")

    return df


def move_policy_id_to_first(df):
    """确保政策ID列为第一列"""
    logger = logging.getLogger('move_policy_id')

    if '政策ID' not in df.columns:
        logger.warning("未找到'政策ID'列，无法调整位置")
        return df

    # 获取所有列名
    all_columns = df.columns.tolist()

    # 如果政策ID不在第一列，则移动到第一列
    if all_columns[0] != '政策ID':
        # 移除现有政策ID列
        all_columns.remove('政策ID')
        # 重新排序：政策ID + 其他列
        new_order = ['政策ID'] + all_columns
        df = df[new_order]
        logger.info("已将'政策ID'列移动到第一列位置")

    return df


def clean_excel_file(input_path, output_path=None):
    """主清洗函数"""
    logger = logging.getLogger('clean_excel')
    start_time = datetime.now()

    if not output_path:
        filename, ext = os.path.splitext(input_path)
        output_path = f"{filename}_cleaned{ext}"

    logger.info(f"开始清洗Excel文件: {input_path}")
    logger.info(f"输出文件: {output_path}")

    try:
        # 读取Excel文件
        logger.info("读取Excel文件...")
        df = pd.read_excel(input_path, engine='openpyxl')
        logger.info(f"成功读取Excel文件: {len(df)} 行, {len(df.columns)} 列")

        # 1. 检查并添加"是否是跨境政策"列
        df = check_and_add_crossborder_column(df)

        # 2. 清洗日期列
        date_columns = ['成文日期', '发文日期', '实施日期', '废止日期']
        df = clean_dates(df, date_columns)

        # 3. 删除空格和空行
        df = remove_whitespace(df)

        # 4. 生成政策ID并放置在第一列
        df = generate_policy_id(df)

        # 5. 政策ID去重
        df = deduplicate_policy_ids(df)

        # 6. 删除标题或正文为空的行
        df = remove_invalid_rows(df)

        # 7. 确保政策ID列为第一列
        df = move_policy_id_to_first(df)

        # 保存清洗后的数据
        logger.info("保存清洗后的Excel文件...")

        # 使用openpyxl保持格式
        wb = openpyxl.Workbook()
        ws = wb.active

        # 添加标题行
        headers = list(df.columns)
        ws.append(headers)

        # 添加数据行
        for row in dataframe_to_rows(df, index=False, header=False):
            ws.append(row)

        # 设置日期格式
        date_style = 'yyyy-mm-dd'
        for col in date_columns:
            if col in headers:
                col_idx = headers.index(col) + 1
                for row in range(2, len(df) + 2):
                    if ws.cell(row=row, column=col_idx).value:
                        ws.cell(row=row, column=col_idx).number_format = date_style

        # 设置政策ID列宽度
        if '政策ID' in headers:
            policy_id_idx = headers.index('政策ID') + 1
            ws.column_dimensions[openpyxl.utils.get_column_letter(policy_id_idx)].width = 30

        # 设置跨境政策列宽度
        if '是否是跨境政策' in headers:
            crossborder_idx = headers.index('是否是跨境政策') + 1
            ws.column_dimensions[openpyxl.utils.get_column_letter(crossborder_idx)].width = 15

        # 保存文件
        wb.save(output_path)

        # 记录处理结果
        duration = datetime.now() - start_time
        logger.info(f"清洗完成! 耗时: {duration}")
        logger.info(f"清洗后数据: {len(df)} 行, {len(df.columns)} 列")

        return output_path

    except Exception as e:
        logger.exception(f"清洗过程中发生错误: {str(e)}")
        return None


if __name__ == "__main__":
    # 配置输入输出文件路径
    INPUT_EXCEL = ["output_政策打标签分表1.xlsx",
                "output_政策打标签分表2.xlsx", "output_政策打标签分表3.xlsx", "output_政策打标签分表4.xlsx",
                   "output_政策打标签分表5.xlsx", "output_政策打标签分表6.xlsx", "output_政策打标签分表7.xlsx",
                   "output_政策打标签分表8.xlsx", "output_政策打标签分表9.xlsx", "output_政策打标签分表10.xlsx", 
                   "output_政策打标签分表11.xlsx"]
    #OUTPUT_EXCEL = "./cleaned_有标签.xlsx"

    # 执行清洗
    for input_file in INPUT_EXCEL:
        cleaned_file = clean_excel_file(input_file, "cleaned_"+input_file)

        if cleaned_file:
            print(f"\n✅ 数据清洗完成! 文件已保存至: {cleaned_file}")
            print("✨ 政策ID列已成功放置在Excel的第一列位置")

            # 检查跨境政策列状态
            df = pd.read_excel(cleaned_file)

        else:
            print("\n❌ 清洗失败，请查看日志文件获取详细信息")